function Player(params) {
  if (!params.video) {
    alert("没有发现video.终止!");
    return;
  }
  this.video = params.video;

  this.canvas = document.createElement("canvas");
  this.webglPlayer = new WebGLPlayer(this.canvas);
  this.video.parentElement.className += " ybl zc";
  this.video.parentElement.insertBefore(this.canvas, this.video);
  this.showLabel = document.getElementById("showLabel");
  this.autoplay = params.autoplay;
  this.precise = params.precise; // 精确跳转.加入此参数后.需要更多时间进行跳转定位
  this.dp = params.dp;

  if (!(params.list && Array.isArray(params.list) && params.list.length)) {
    alert("没有播放列表list.终止!");
    return;
  }

  this.icoL = document.getElementsByClassName("diplayer-loading-icon")[0];
  this.icoP = document.getElementsByClassName("dplayer-mobile-play")[0];
  this.setLoading = false;
  this.apf = () => {
    this.apf = null;
    this.loading(false, true);
    this.setLoading = false;
    this.lp = this.autoplay;
  }


  let t = document.getElementsByTagName("script");
  t = t[t.length - 1].src;
  const path = params.jsLibPath ? params.jsLibPath : t.substr(0, t.lastIndexOf("/") + 1);

  if (params.decoderJsPath) {
    if (params.decoderJsPath.substr(0, 1) == ".")
      params.decoderJsPath = path + params.decoderJsPath.substr(1);
  } else params.decoderJsPath = path + "decoder.js";
  this.decoderJsPath = params.decoderJsPath;

  if (params.libJsPath) {
    if (params.libJsPath.substr(0, 1) == ".")
      params.libJsPath = path + params.libJsPath.substr(1);
  } else params.libJsPath = path + "lib.js";
  this.libJsPath = params.libJsPath;

  if (params.downloadJsPath) {
    if (params.downloadJsPath.substr(0, 1) == ".")
      params.downloadJsPath = path + params.downloadJsPath.substr(1);
  } else params.downloadJsPath = path + "downloader.js";

  this.decoderPackCallback = [];
  this.continueDecoderCallback = [];
  this.pauseDecoderCallback = [];
  this.workerDecoderContinue = 0;

  this.workerDownloader = new Worker(params.downloadJsPath); // 下载线程
  this.workerDownloader.onmessage = this.downloadMsg.bind(this);
  this.init(params.list);
  this.re = 0;
  this.rr = 0;
  this.displayVideo();

  // this.displayAudio();
}
Player.prototype.init = function (list) {
  delete this.list;
  this.list = [];
  list.map(a => {
    this.list.push({ i: this.list.length, url: a.url.toString(), start: a.start, end: a.end });
  });

  this.playerState = 1; // 自身状态
  this.i = 0;
  this.lastTime = this.list[0].start;
  this.currentTime = this.lastTime;
  this.nextTime = this.list[0].end;
  this.ov = 0;

  this.next = false;
  this.playing = true;

  if (this.v) delete this.v;
  this.v = []; // 视频数据
  this.downloadCallback = {};
  this.autoDecoder();
};
Player.prototype.formatTime = function (s) {
  var h = Math.floor(s / 3600) < 10 ? '0' + Math.floor(s / 3600) : Math.floor(s / 3600);
  var m = Math.floor((s / 60 % 60)) < 10 ? '0' + Math.floor((s / 60 % 60)) : Math.floor((s / 60 % 60));
  var s = Math.floor((s % 60)) < 10 ? '0' + Math.floor((s % 60)) : Math.floor((s % 60));
  return result = h + ":" + m + ":" + s;
};
Player.prototype.seek = function (params) {
  let i = this.time2i(params.time);
  if (i == this.i || i < 0) {
    // console.log("seek返回了", i);
    return true;
  }
  this.autoPlay();
  this.created = false;
  this.next = false;
  this.setLoading = true;
  this.i = i;
  this.pauseDecoder({
    callback: a => {
      // console.log("暂停", a);
      this.v.splice(0);
      this.autoDecoder((b) => {
        this.dumpVideo();
        this.playerState = 1;
        // console.log("seek解压完毕");
        if (typeof params.callback == "function") params.callback(b);
        this.created = true;
      });
    }, real: true
  });
};
Player.prototype.seekTo = function (callback) {
  this.workerDecoderContinue = 0;
  this.next = 0;
  this.seekToCallback = (s) => {
    if (typeof callback == "function") callback(s);
    this.workerDecoderContinue = -2;
  };
  this.workerDecoder.postMessage({ e: MsgDecoder.seekTo, time: this.currentTime/* , da: this.a.length ? 0 : 1 */ });
}
Player.prototype.displayVideo = function () {
  if (this.showLabel) {
    let t = "v:" + this.v.length + "d:" + typeof this.list[this.i].d + " nd:" + typeof this.list[this.i + 1 < this.list.length ? this.i + 1 : 0].d;

    t += "<br/>n:" + this.i + " re:" + this.re + " rr:" + this.rr;
    this.showLabel.innerHTML = t;
  }
  this.currentTime = this.video.currentTime;
  let t = Math.abs(this.currentTime - this.lastTime);
  if (this.playerState) {
    if (this.v.length) {
      let ti = this.currentTime - this.v[0].s + this.ov;
      if (ti >= 0) {
        this.lastTime = this.currentTime;
        this.webglPlayer.renderFrame(this.v[0].d, this.videoWidth, this.videoHeight, this.yLength, this.uvLength);
        this.v.shift();
        if (ti > 1) this.vl++;
        else this.vl = 0;
      } else {
        this.vl = 0;
        if (this.apf) this.apf();
      }
    } else {
      this.vl++;
    }
    if (this.vl > 60 && !this.video.paused) {
      this.vl = 0;
      this.autoPlay();
    }
  }
  if (t >= 2) {
    // console.log(">2");
    this.rr++;
    if (this.seek({
      time: this.currentTime, callback: a => {
        if (this.precise) {
          this.continueDecoder({
            callback: b => {
              this.skipVideo(c => {
                this.webglPlayer.renderFrame(c.d, this.videoWidth, this.videoHeight, this.yLength, this.uvLength);
                requestAnimationFrame(this.displayVideo.bind(this));
              });
            }, real: true
          });
        } else {
          this.video.currentTime = this.v[0].s;
          this.currentTime = this.v[0].s;
          requestAnimationFrame(this.displayVideo.bind(this));
        }
      }
    })) requestAnimationFrame(this.displayVideo.bind(this));
    return;
  } else if (this.created) {
    if (this.v.length < 50) {
      if (this.workerDecoderContinue === -2) {
        // console.log("缓存不够,发送继续");
        this.continueDecoder({});
      } else if (this.next) {
        // console.log("下一个", this.i, this.list.length);
        this.i++;
        if (this.i < this.list.length) {
          this.autoDecoder();
        } else {
          // // console.log('超出');
          this.next = false;
          // this.playing = false;
        }

      } else if (this.nextTime < this.currentTime && this.i) {
        this.i++;
        if (this.i < this.list.length) {
          console.log("跳帧2", this.i, this.currentTime, t, this.video.duration);
          this.autoDecoder();
        } else {
          console.log("播放结束", this.dp.setting.loop);
          this.playing = this.dp.setting.loop;
          if (!this.playing) this.video.pause();
          this.video.currentTime = 0;
        }
      }
    }
  }
  requestAnimationFrame(this.displayVideo.bind(this));
  // console.log(this.lp, this.v.length);
  if (this.lp && this.v.length > 50) {
    this.lp = false;
    try {
      if (this.playing) this.dp.play();
      else this.playing = true;
    } catch (error) {

    }
    this.loading(false, true);
  }
  if (this.v.length > 100 && this.workerDecoderContinue == 2) {
    // console.log("防止内存溢出");
    this.pauseDecoder({});
  }
};
Player.prototype.dumpVideo = function () {
  let i = 0, j = 1, l = this.v.length;
  while (j < l) {
    if (this.v[i].s > this.v[j].s) {
      this.v.splice(i, 1);
      l--;
    } else {
      i = j;
      j++;
    }
  }
};
Player.prototype.skipVideo = function (callback, tmp) {
  if (this.v[0] && this.v[0].s > this.currentTime) {
    if (typeof callback == "function") {
      callback(tmp ? tmp : this.v[0]);
    }
    return;
  }
  const t = this.v.shift();
  if (t) tmp = t;
  // console.log(tmp.s, this.currentTime, this.v.length);
  requestAnimationFrame(this.skipVideo.bind(this, callback, tmp));
};
Player.prototype.downloadMsg = function (evt) {
  let c = 0;
  switch (evt.data.e) {
    case MsgDownloader.download:
      this.list[evt.data.i].d = evt.data.d; // 存储文件
      c = this.downloadCallback[evt.data.i];
      delete this.downloadCallback[evt.data.i];
      if (typeof c == "function") c(this.list[evt.data.i]);
      break;
    case MsgDownloader.fileInfo:
      console.log("解析", evt.data.list);
      // this.useIphone(evt.data.list);
      break;
    default:
      console.warn("下载线程返回其他指令:", evt.data);
      break;
  }
};
Player.prototype.download = function (i, callback) {
  if (i < 0) return;
  this.list[i].e = MsgDownloader.download;
  let d = this.list[i];
  if (typeof callback == "function") this.downloadCallback[i] = callback;
  this.workerDownloader.postMessage(d); // 发送下载命令
};
Player.prototype.sureSnBuff = function (i, callback) {
  if (i >= this.list.length) i = 0;
  if (this.list[i].d) {
    if (typeof callback == "function") callback(this.list[i]);
  } else {
    if (this.list[i].e === MsgDownloader.download && this.downloadCallback[i]) {
      if (typeof callback == "function") this.downloadCallback[i] = callback;
    } else this.download(i, callback);
  }
};
Player.prototype.sendDecoder = function (i, callback) {
  this.next = false;
  this.sureSnBuff(i, (a) => {
    this.decoderPackCallback.push(callback);
    this.list[i].e = MsgDecoder.decoderPack;
    this.workerDecoder.postMessage(this.list[i], [this.list[i].d.buffer]);
    delete this.list[i].d;
    delete this.list[i].offsetTime;
    i++;
    this.sureSnBuff(i < this.list.length ? i : 0);
  });
};
Player.prototype.autoDecoder = function (callback) {
  this.next = false;
  this.created = false;
  if (this.i >= this.list.length || this.i < 0) this.i = 0;
  this.nextTime = this.list[this.i].end;
  this.createDecoder(() => {
    this.sendDecoder(this.i, a => {
      // console.log("解码", a);
      this.seekTo(callback);
    });
  });
};
Player.prototype.delDecoder = function () {
  this.created = false;
  this.next = false;
  this.workerDecoderContinue = 0;
  if (this.workerDecoder) {
    this.workerDecoder.terminate();
    delete this.workerDecoder;
  }
};
Player.prototype.createDecoder = function (callback, param) {
  this.delDecoder();
  if (this.workerDecoderNext) {
    this.workerDecoder = this.workerDecoderNext;
    this.created = true;
    delete this.workerDecoderNext;
    if (typeof callback == "function") callback(param);
  } else {
    this.createdCallback = () => { if (typeof callback == "function") callback(param); };
    this.workerDecoder = new Worker(this.decoderJsPath);
    this.workerDecoder.onmessage = this.onmessage.bind(this);
  }
  this.onmessageNext({ data: { e: MsgDecoder.reload } });
};
Player.prototype.pauseDecoder = function (params) {
  if ((this.workerDecoder && this.workerDecoderContinue === 2) || params.real) {
    this.workerDecoderContinue = -1;
    this.pauseDecoderCallback.push(params.callback);
    this.workerDecoder.postMessage({ e: MsgDecoder.pause, c: !!params.callback });
  }
};
Player.prototype.continueDecoder = function (params) {
  if ((this.workerDecoder && this.workerDecoderContinue === -2) || params.real) {
    this.workerDecoderContinue = 1;
    this.continueDecoderCallback.push(params.callback);
    this.workerDecoder.postMessage({ e: MsgDecoder.continue, c: !!params.callback });
  }
};
Player.prototype.autoPlay = function (params) {
  this.apf = () => {
    this.apf = undefined;
    this.lp = true;
  };
  this.video.pause();
  this.loading(true, true);
};

Player.prototype.onmessageNext = function (evt) {
  switch (evt.data.e) {
    case MsgDecoder.created:
      // console.log("第二解码器创建完毕", evt.data);
      if (this.workerDecoderNextTmp) {
        this.workerDecoderNextTmp.onmessage = this.onmessage.bind(this);
        this.workerDecoderNext = this.workerDecoderNextTmp;
        delete this.workerDecoderNextTmp;
      }
      break;
    case MsgDecoder.reload:
      // console.log("第二解码器需要重载");
      if (this.workerDecoderNext) {
        this.workerDecoderNext.terminate();
        delete this.workerDecoderNext;
      }
      this.workerDecoderNextTmp = new Worker(this.decoderJsPath);
      this.workerDecoderNextTmp.onmessage = this.onmessageNext.bind(this);
      break;

    default:
      console.log("第二解码器其他消息", evt.data);
      break;
  }
};
Player.prototype.onmessage = function (evt) {
  // console.log('解码器回调', evt.data);
  let t = 0, param = evt.data.param;
  switch (evt.data.e) {
    case MsgDecoder.videoCallback:
      this.v.push({ s: evt.data.s, d: evt.data.d });
      break;
    case MsgDecoder.audioCallback:
      this.a.push({ s: evt.data.s, d: evt.data.d });
      break;
    case MsgDecoder.decoderPack: // 解码一个ts片段
      // console.log("片段完毕", evt.data);
      // console.log('时间', (new Date() - this.ttm) / 1000, this.next);
      this.workerDecoderContinue = -2;
      this.next = evt.data.real;
      t = this.decoderPackCallback.shift();
      if (typeof t == "function") {
        t(evt.data);
      }
      break;
    case MsgDecoder.initVideo: // 初始化视频参数
      delete evt.data.e;
      if (this.ov) return;
      switch (evt.data.a.f) {
        case 0:
          this.audioEncoding = "8bitInt";
          break;
        case 1:
          this.audioEncoding = "16bitInt";
          break;
        case 2:
          this.audioEncoding = "32bitInt";
          break;
        case 3:
          this.audioEncoding = "32bitFloat";
          break;
        default:
          this.audioEncoding = "16bitInt";
          console.warn("没有找到音频解码.默认16bitInt");
      }
      this.audioChannels = evt.data.a.c;
      this.audioSampleRate = evt.data.a.r;
      this.restartAudio();

      this.videoWidth = evt.data.v.w;
      this.videoHeight = evt.data.v.h;
      this.yLength = this.videoWidth * this.videoHeight;
      this.uvLength = (this.videoWidth / 2) * (this.videoHeight / 2);
      // evt.data.duration = this.duration;
      // if (!this.ov) this.ov = 0 - evt.data.v0;
      // console.log("初始化", evt.data);
      this.videoLoaded(evt.data);
      break;
    case MsgDecoder.created:
      this.created = true;
      t = this.createdCallback;
      delete this.createdCallback;
      if (typeof t == "function") t(evt.data);
      break;
    case MsgDecoder.reload:
      this.re++;
      this.autoPlay();
      t = this.showLabel;
      this.showLabel = undefined;
      t.innerHTML = JSON.stringify(evt.data);
      console.log('重载', evt.data);
      this.created = false;
      this.createDecoder(() => {
        if (param) {
          this.v.splice(0);
          // console.log('主', param);
          delete this.createdCallback;
          this.workerDecoder.postMessage(param, [param.d.buffer]);
        }
      });
      break;
    case MsgDecoder.continue:
      this.workerDecoderContinue = 2;
      t = this.continueDecoderCallback.shift();
      if (typeof t == "function") {
        t(evt.data);
      }
      break;
    case MsgDecoder.pause:
      this.workerDecoderContinue = -2;
      t = this.pauseDecoderCallback.shift();
      if (typeof t == "function") {
        t(evt.data);
      }
      break;
    case MsgDecoder.fileIn:
      t = new Blob([evt.data.d], { type: "video/mp2t", name: '0.ts' });
      console.log(t, evt.data, URL.createObjectURL(t));
      break;
    case MsgDecoder.seekTo:
      if (this.seekToCallback) {
        t = this.seekToCallback;
        delete this.seekToCallback;
        t(evt.data);
      }
      break;
    default:
      console.warn("解码器其他消息", evt.data);
      break;
  }
};
Player.prototype.restartAudio = function () {
  return;
  if (this.pcmPlayer) {
    this.pcmPlayer.destroy();
    delete this.pcmPlayer;
  }
  this.pcmPlayer = new PCMPlayer({
    encoding: this.audioEncoding,
    channels: this.audioChannels,
    sampleRate: this.audioSampleRate,
    flushingTime: 1000
  });

};
Player.prototype.time2i = function (time, r0) {
  let i = 0;
  let j = this.list.length - 1;
  while (i < j && this.list[i].start < time) {
    i++;
    if (this.list[i].start >= time) {
      i--;
      break;
    }
  }
  if (i >= this.list.length) {
    i = r0 ? 0 : -1;
  }
  return i;
};

Player.prototype.rotate = function (positive) {
  this.canvas.style.transform = positive ? "rotate(90deg)" : "";
};
Player.prototype.delSmall = function (params) {
  let t = false, i = 0, j = 1, l = params.length;
  while (j < l) {
    if (params[i].s > params[j].s) {
      params.splice(i, 1);
      l--;
    } else {
      i = j;
      j++;
    }
  }
  return params;
};
Player.prototype.loading = function (params, real) {
  if ((this.setLoading || real) && this.icoL) {
    if (params) {
      this.icoL.style.display = "block";
      this.icoP.style.display = "none";
    } else {
      if (this.currentTime > 0) {
        this.icoL.style.display = null;
        this.icoP.style.display = null;
      } else {
        if (this.tmpHandle) clearTimeout(this.tmpHandle);
        this.tmpHandle = setTimeout(() => {
          clearTimeout(this.tmpHandle);
          this.tmpHandle = null;
          this.icoL.style.display = null;
          this.icoP.style.display = null;
        }, 500);
      }
    }
  }
};
Player.prototype.videoLoaded = function (d) {
  this.ov = this.v[0].s
  console.log("视频初始化完毕", this.offsetTime);
  this.canvas.style["max-width"] = "100%";
  this.canvas.style["max-height"] = "100%";
  this.canvas.width = d.v.w;
  this.canvas.height = d.v.h;

};
const MsgDownloader = {
  get error() { return -1; }, // 出现错误
  get fileInfo() { return 0; }, // 获取文件信息 保持与初始化一致,因为下一步就是初始化
  get download() { return 1; }, // 下载文件
  get dataIn() { return 2; }, // 数据到达
};
const MsgDecoder = {
  get error() { return -1; }, // 出现错误
  get initDecoder() { return 0 }, // 解码器已初始化 尽可能与下载文件一致.减少写数据
  get uninitDecoder() { return -10002 }, // 解码器已卸载
  get openDecoder() { return -10003 }, // 打开解码器
  get closeDecoder() { return -10004 }, // 关闭解码器
  get startDecoding() { return -10005 }, // 解码操作
  get audioCallback() { return -10006 }, // 音频数据
  get videoCallback() { return -10007 }, // 视频数据
  get requestCallback() { return -10008 }, // 请求数据
  get seekTo() { return -10009 }, // 跳转时间
  get sendData() { return -10010 }, // 发送数据
  get simpleDecode() { return -10011 }, // 简易解码 包含初始化等一键式解码
  get testApi() { return -10012 }, // 
  get offsetTime() { return -10013 }, // 
  get decoderOnePacket() { return -10014 }, // 
  get decoderPack() { return -10015 }, // 
  get initVideo() { return -10016 }, // 
  get created() { return -10017 }, // 
  get reload() { return -10018 }, // 
  get continue() { return -10019 }, // 
  get pause() { return -10020 }, // 
  get setOffsetTime() { return -10021 }, // 
  get fileIn() { return -10022 }, // 解码器已卸载
};
